'abuentity.monkey by Belimoth

'TODO
'	Finalize() methods for all components
'	put the ITweener aspect inside of a component
'	overloading ZIndex property must be accounted for (no accessing _zIndex directly)

Strict
Import abucomponent
Import abupositioncomponent
Import abusizecomponent
Import abuvelocitycomponent
Import abuphysicscomponent
Import abuspritecomponent
Import aburendercomponent
Import abuschedulecomponent



Interface IEntity Extends IPosition, ISize, IVelocity, IPhysics, ISprite, IRender, ISchedule
	Method PositionComponent:IPosition() Property
	Method SizeComponent:ISize() Property
	Method VelocityComponent:IVelocity() Property
	Method PhysicsComponent:IPhysics() Property
	Method SpriteComponent:ISprite() Property
	Method ScheduleComponent:ISchedule() Property
End



Class AbuEntity Extends EntityBuilder<PositionComponent, SizeComponent, VelocityComponent, PhysicsComponent, SpriteComponent, SpriteRender, ScheduleComponent>
	Method AddTween:Void(name:String, value:Float, duration:Int, equation:TweenEquationCall = Tween.Linear)
		Local tweenNew := New AbuTween(Self, name, value, duration, equation)
		Local tweenReplaced := _tweens.Get(name)
		If tweenReplaced <> Null Then tweenReplaced.Destroy()
		_tweens.Set(name, tweenNew)
	End
	
	Method PauseTween:Void(name:String)
		_tweens.Get(name).Pause()
	End
	
	Method ResumeTween:Void(name:String)
		_tweens.Get(name).Resume()
	End
	
	Method RemoveTween:Void(name:String)
		_tweens.Get(name).Destroy()
		_tweens.Remove(name)
	End
	
	Method ClearTweens:Void()
		For Local tween := Eachin _tweens.Values()
			tween.Destroy()
		Next
		_tweens.Clear()
	End
	
	Method OnTweenComplete:Void(name:String)
	
	End
	
Private
	Field _tweens := New StringMap<AbuTween>
End



Class EntityBase
	Method Update:Void()
		
	End
	
	Method Finalize:Void()
		
	End
End



Class EntityBuilder<CPosition, CSize, CVelocity, CPhysics, CSprite, CRender, CSchedule> Extends EntityBase Implements IEntity

Private
	Field _markedForDeletion:Bool		'not implemented yet
	
Public	
	Method InitializeComponents:Void()
		positionComponent.Initialize(Self)
		velocityComponent.Initialize(Self)
		sizeComponent.Initialize(Self)
		physicsComponent.Initialize(Self)
		spriteComponent.Initialize(Self)
		renderComponent.Initialize(Self)
		scheduleComponent.Initialize(Self)
	End
	
	Method Finalize:Void()
		positionComponent.Finalize()
		velocityComponent.Finalize()
		sizeComponent.Finalize()
		physicsComponent.Finalize()
		spriteComponent.Finalize()
		renderComponent.Finalize()
		scheduleComponent.Finalize()
	End
	
	Method Destroy:Void()
		_markedForDeletion = True
		_queuedForDeletion.AddLast(Self)
		'TODO do more than this
	End
	
	'#Region IPosition
	Field positionComponent := New CPosition()'_________________________________________________IPosition component________________________________________________________
	Method PositionComponent:IPosition() Property												Return positionComponent;												End
	Method PositionComponent:Void(component:CPosition) Property									positionComponent.Finalize(); positionComponent = component;			End	
	Method X:Int() Property																		Return positionComponent.X;												End
	Method X:Void(xNew:Int) Property															positionComponent.X = xNew;												End
	Method Y:Int() Property																		Return positionComponent.Y;												End
	Method Y:Void(yNew:Int) Property															positionComponent.Y = yNew;												End
	Method Position:Position() Property															Return positionComponent.Position;										End
	Method SetPosition:Void(xNew:Int, yNew:Int)													positionComponent.SetPosition(xNew, yNew);								End
	Method DistanceTo:Float(other:IPosition)													Return positionComponent.DistanceTo(other);								End
	Method AngleTo:Float(other:IPosition)														Return positionComponent.AngleTo(other);								End
	'#EndRegion

	'#Region IVelocity
	Field velocityComponent := New CVelocity()'_________________________________________________IVelocity component________________________________________________________
	Method VelocityComponent:IVelocity() Property												Return velocityComponent;												End
	Method VelocityComponent:Void(component:CVelocity) Property									velocityComponent.Finalize(); velocityComponent = component;			End	
	Method XVelocity:Float() Property															Return velocityComponent.XVelocity;										End
	Method XVelocity:Void(xVelocityNew:Float) Property											velocityComponent.XVelocity = xVelocityNew;								End
	Method YVelocity:Float() Property															Return velocityComponent.YVelocity;										End
	Method YVelocity:Void(yVelocityNew:Float) Property											velocityComponent.YVelocity = yVelocityNew;								End
	Method XAcceleration:Float() Property														Return velocityComponent.XAcceleration;									End
	Method XAcceleration:Void(xAccelerationNew:Float) Property									velocityComponent.XAcceleration = xAccelerationNew;						End
	Method YAcceleration:Float() Property														Return velocityComponent.YAcceleration;									End
	Method YAcceleration:Void(yAccelerationNew:Float) Property									velocityComponent.YAcceleration = yAccelerationNew;						End
	Method XMaxVelocity:Float() Property														Return velocityComponent.XMaxVelocity;									End
	Method YMaxVelocity:Float() Property														Return velocityComponent.YMaxVelocity;									End
	Method SetMaxVelocity:Void(xMaxVelocityNew:Float, yMaxVelocityNew:Float)					velocityComponent.SetMaxVelocity(xMaxVelocityNew, yMaxVelocityNew);		End
	Method XDisplacement:Int() Property															Return velocityComponent.XDisplacement;									End
	Method YDisplacement:Int() Property															Return velocityComponent.YDisplacement;									End
	'#EndRegion
	
	'#Region ISize
	Field sizeComponent := New CSize()'_________________________________________________________ISize component____________________________________________________________
	Method SizeComponent:ISize() Property														Return sizeComponent;													End
	Method SizeComponent:Void(component:CSize) Property											sizeComponent.Finalize(); sizeComponent = component;					End	
	Method Width:Int() Property																	Return sizeComponent.Width;												End
	Method Width:Void(widthNew:Int) Property													sizeComponent.Width = widthNew;											End
	Method Height:Int() Property																Return sizeComponent.Height;											End
	Method Height:Void(heightNew:Int) Property													sizeComponent.Height = heightNew;										End
	Method Center:Position() Property															Return sizeComponent.Center;											End
	Method Center:Void(center:Position) Property												sizeComponent.Center = center											End
	Method SetWidth:Void(widthNew:Int, align:Int = ALIGN_LEFT)									sizeComponent.SetWidth(widthNew, align);								End
	Method SetHeight:Void(heightNew:Int, align:Int = ALIGN_TOP)									sizeComponent.SetHeight(heightNew, align);								End
	Method SetSize:Void(widthNew:Int, heightNew:Int, align:Int = ALIGN_LEFT|ALIGN_TOP)			sizeComponent.SetSize(widthNew, heightNew, align);						End
	Method GetEdge:Int(direction:Int)															Return sizeComponent.GetEdge(direction);								End
	'#EndRegion
	
	'#Region IPhysics	
	Field physicsComponent := New CPhysics()'___________________________________________________IPhysics component_________________________________________________________
	Method PhysicsComponent:IPhysics() Property													Return physicsComponent;												End
	Method PhysicsComponent:Void(component:CPhysics) Property									physicsComponent.Finalize(); physicsComponent = component;				End
	Method Solid:Bool() Property																Return physicsComponent.Solid											End
	Method Solid:Void(solid:Bool) Property														physicsComponent.Solid = solid											End	
	Method CollisionGroup:Int() Property														Return physicsComponent.CollisionGroup;									End
	Method CollisionGroup:Void(group:Int) Property												physicsComponent.CollisionGroup = group;								End
	Method CollisionMask:Int() Property															Return physicsComponent.CollisionMask;									End
	Method CollisionMask:Void(mask:Int) Property												physicsComponent.CollisionMask = mask;									End
	Method Move:Void(x:Int, y:Int)																physicsComponent.Move(x, y);											End
	Method IsTouching:Bool(direction:Int)														Return physicsComponent.IsTouching(direction);							End
	Method OnCollision:Void(other:IEntity) End													'meant to be overridden													End
	'#EndRegion
	
	'#Region ISprite
	Field spriteComponent := New CSprite()'_____________________________________________________ISprite component__________________________________________________________
	Method SpriteComponent:ISprite() Property													Return spriteComponent;													End
	Method SpriteComponent:Void(component:CSprite) Property										spriteComponent.Finalize(); spriteComponent = component;				End	
	Method CurrentAnimation:String() Property													Return spriteComponent.CurrentAnimation;								End
	Method NextAnimation:String() Property														Return spriteComponent.NextAnimation;									End
	Method LoopCount:Int() Property																Return spriteComponent.LoopCount;										End
	Method HandleX:Float() Property																Return spriteComponent.HandleX;											End
	Method HandleY:Float() Property																Return spriteComponent.HandleY;											End
	Method SetHandle:Void(x:Float, y:Float)														spriteComponent.SetHandle(x, y);										End
	Method AnimationSheet:AbuAnimationSheet() Property											Return spriteComponent.AnimationSheet;									End
	Method AnimationSheet:Void(animationSheet:AbuAnimationSheet) Property						spriteComponent.AnimationSheet(animationSheet);							End
	Method SetAnimationSheet:Void(path:String, frameWidth:Int, frameHeight:Int)					spriteComponent.SetAnimationSheet(path, frameWidth, frameHeight);		End
	Method SetRate:Void(rate:Float)																spriteComponent.SetRate(rate);											End	
	Method AddAnimation:Void(name:String, sequence:Int[], frameDuration:Int, loop:Bool = True, flipX:Bool = False, flipY:Bool = False)									'..
		spriteComponent.AddAnimation(name, sequence, frameDuration, loop, flipX, flipY);																				End
	Method AddAnimation:Void(name:String, sequence:Int[], frameDuration:Int[], loop:Bool = True, flipX:Bool = False, flipY:Bool = False)								'..
		spriteComponent.AddAnimation(name, sequence, frameDuration, loop, flipX, flipY);																				End
	Method AddAnimation:Void(name:String, start:Int, sequence:Int[], frameDuration:Int, loop:Bool = True, flipX:Bool = False, flipY:Bool = False)						'..
		spriteComponent.AddAnimation(name, start, sequence, frameDuration, loop, flipX, flipY);																			End
	Method AddAnimation:Void(name:String, start:Int, sequence:Int[], frameDuration:Int[], loop:Bool = True, flipX:Bool = False, flipY:Bool = False)						'..
		spriteComponent.AddAnimation(name, start, sequence, frameDuration, loop, flipX, flipY);																			End
	Method AddAnimation:Void(name:String, subAnimations:String[], loop:Bool = True, flipX:Bool = False, flipY:Bool = False)												'..
		spriteComponent.AddAnimation(name, subAnimations, loop, flipX, flipY);																							End
	Method PlayAnimation:Void(name:String, rate:Float = 1.0)									spriteComponent.PlayAnimation(name, rate);								End
	Method QueueAnimation:Void(name:String, rate:Float = 1.0)									spriteComponent.QueueAnimation(name, rate);								End
	Method PlayNextAnimation:Void()																spriteComponent.PlayNextAnimation();									End
	Method OnAnimationLoop:Void(animation:String) End											'meant to be overridden													'''
	Method OnAnimationEnd:Void(animation:String) End											'meant to be overridden													'''
	Method AddOffset:Void(name:String, offsetX:Int[], offsetY:Int[], cumulative:Bool = False)	spriteComponent.AddOffset(name, offsetX, offsetY, cumulative);			End
	Method AddDisplacement:Void(name:String, displaceX:Int[], displaceY:Int[])					spriteComponent.AddDisplacement(name, displaceX, displaceY);			End
	Method _Displace:Void(x:Int, y:Int) End														'called at the AbuSpriteComponent level									'''
	Method Draw:Void(x:Int, y:Int, rotation:Float = 0, scaleX:Float = 1, scaleY:Float = 1)		spriteComponent.Draw(x, y, rotation, scaleX, scaleY);					End
	'#EndRegion
	
	'#Region IRender
	Field renderComponent := New CRender()'_____________________________________________________IRender component__________________________________________________________
	Method RenderComponent:IRender() Property													Return renderComponent;													End
	Method RenderComponent:Void(component:CRender) Property										renderComponent.Finalize(); renderComponent = component;				End
	Method Visible:Bool() Property																Return renderComponent.Visible;											End
	Method Visible:Void(visible:Bool) Property													renderComponent.Visible = visible;										End
	Method Alpha:Float() Property																Return renderComponent.Alpha;											End
	Method Alpha:Void(alpha:Float) Property														renderComponent.Alpha = alpha;											End
	Method ZIndex:Int() Property																Return renderComponent.ZIndex;											End
	Method ZIndex:Void(zIndex:Int)																renderComponent.ZIndex = zIndex;										End
	Method Render:Void()																		renderComponent.Render();												End
	'#EndRegion
	
	'#Region ISchedule
	Field scheduleComponent := New CSchedule()'_________________________________________________ISchedule component________________________________________________________
	Method ScheduleComponent:ISchedule() Property												Return scheduleComponent;												End
	Method ScheduleComponent:Void(component:CSchedule) Property									scheduleComponent.Finalize(); scheduleComponent = component;			End
	Method AddTween:Void(name:String, value:Float, duration:Int, equation:TweenEquationCall = Tween.Linear)																'..
		scheduleComponent.AddTween(name, value, duration, equation);																									End
	Method PauseTween:Void(name:String)															scheduleComponent.PauseTween(name);										End
	Method ResumeTween:Void(name:String)														scheduleComponent.ResumeTween(name);									End
	Method RemoveTween:Void(name:String)														scheduleComponent.RemoveTween(name);									End
	Method ClearTweens:Void()																	scheduleComponent.ClearTweens();										End
	Method OnTweenComplete:Void(name:String) End												'meant to be overridden													'''
	'#EndRegion
End



Private
	Global _queuedForDeletion := New List<EntityBase>

Public

Function UpdateEntities:Void()
	'TODO actually call EntityBase.Update for every EntityBase
	For Local entity := EachIn _queuedForDeletion
		entity.Finalize()
		_queuedForDeletion.RemoveEach(entity)
	Next
End